home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus 2004 #2 / Amiga Plus CD - 2004 - No. 02.iso / AmigaPlus / Tools / Development / AmigaTalk / general / BufferedValueHolder.st < prev    next >
Encoding:
Text File  |  2004-01-31  |  9.0 KB  |  282 lines

  1. " -----------------------------------------------------------------
  2.   Class BufferedValueHolder is a wrapper for a ValueModel (the 
  3.   subject).  Clients see the current value of the subject until 
  4.   value: provides a new value.  The new value is not provided to 
  5.   the subject until the application directs it via a setting the 
  6.   triggerChannel value to true.  The buffered value may be discarded 
  7.   by setting the trigger channel value to false.
  8.  
  9.   Instance Variables:
  10.      subject <ValueModel> The ultimate source/destination of the value.
  11.      
  12.      triggerChannel <ValueModel> When this changes, push the current
  13.                                  value down to the subject.  If the
  14.                     value is equal to notYetAssigned, do nothing.
  15.  
  16.    Class Variables:
  17.      notYetAssigend <Object> A distinguished value used to 
  18.                              indicate that value has not been set.
  19.  
  20.    A BufferedValueHolder is used to hold a temporary copy of the 
  21.    value in another valueModel (known as the subject).  The 
  22.    application modifies the temporary copy, but the Buffered-
  23.    ValueHolder only gives this temporary value to its subject when 
  24.    the application confirms the changes.  The application also has 
  25.    the option of canceling the changes, resetting the temporary 
  26.    copy to the subject's value. 
  27.  
  28.    For example, suppose the application provides a series of input
  29.    fields for entering customer name, address, phone, etc., but we 
  30.    only want the Customer object to be updated after the user has 
  31.    finished entering data and has indicated completion by clicking 
  32.    on an OK button.  This technique is often used in database 
  33.    applications, to postpone updating the customer record in the 
  34.    database until all changes to that record are completed.  In this
  35.    application, the customer's old address would likely be held by 
  36.    an AspectAdaptor on the Customer object.  The aspect adaptor would 
  37.    become the subject of a BufferedValueHolder.  The Buffered-
  38.    ValueHolder would make a temporary copy of the customer's address 
  39.    and make that value available to the input field for editing. 
  40.    The user could change the address, but so far only the temporary 
  41.    copy has been altered.  Only when the users clicks on 'OK' does 
  42.    the application notify each field's BufferedValueHolder to replace 
  43.    the corresponding value in the Customer object. 
  44.    
  45.    A BufferedValueHolder is created by sending a #subject:triggerChannel: 
  46.    message to this class.  The subject is a valueModel containing the 
  47.    data value.  The triggerChannel is a ValueHolder containing the 
  48.    boolean object false.  Later, when the user clicks on 'OK', the 
  49.    application can cause the temporary copy to become the subject's 
  50.    value by setting the triggerChannel's value to true.  The 
  51.    application can also cancel any edits, by setting the trigger-
  52.    Channel's value to false.  Note that the prior value in the 
  53.    triggerChannel is not significant -- setting the value to true 
  54.    when it is already true has the same effect as if it were 
  55.    previously false. 
  56.    
  57.    By using the same triggerChannel for all of the Buffered-
  58.    ValueHolders, the application can cause them all to be updated 
  59.    at the same time.  This is the usual arrangement for a set of 
  60.    related widgets. 
  61.   ------------------------------------------------------------------
  62. "
  63.  
  64. " ------------------------------------------------------------------ "
  65. " This class exists because Little Smalltalk does not have Class     "
  66. " variables available.                                               " 
  67. " ALL singleton classes MUST contain the following:                  "
  68. ""
  69. "   the methods:  isSingleton AND privateSetup     AND               "
  70. "                 uniqueInstance Class instance variable.            "
  71. " ------------------------------------------------------------------ "
  72.  
  73. Class BVHGlobalVar :Object ! uniqueInstance !
  74. [
  75.    isSingleton
  76.      
  77.       ^ true
  78. |
  79.    privateNew
  80.  
  81.      ^ super new
  82. |
  83.    new
  84.      ^ (self privateSetup)
  85. |
  86.    privateSetup
  87.  
  88.      (uniqueInstance isNil)
  89.        ifTrue: [uniqueInstance  <- self privateNew ].
  90.        
  91.      ^ self
  92. |
  93.    notYetAssigned
  94.  
  95.       ^ uniqueInstance
  96. |
  97.    notYetAssigned: aBoolean
  98.  
  99.       uniqueInstance <- aBoolean
  100. ]
  101.  
  102. Class BufferedValueHolder :ValueHolder 
  103. ! value subject triggerChannel na !
  104. [
  105.    subject: aSubject triggerChannel: aTrigger
  106.       " Create a new BufferedValueHolder which provides buffering
  107.       * for the ValueModel aSubject, and which pushes the buffered
  108.       * value into the subject when the ValueModel aTrigger changes.
  109.       "
  110.       ^ (self new)
  111.                  subject: aSubject;
  112.           triggerChannel: aTrigger
  113. |
  114.    initialize
  115.  
  116.       na    <- BVHGlobalVar new.
  117.  
  118.       super initialize.
  119.  
  120.       value <- na notYetAssigned
  121. |
  122.    releaseParts
  123.  
  124.       " Remove the receiver as dependents of the triggerChannel
  125.       * and subject.
  126.       "
  127.       (triggerChannel notNil)
  128.          ifTrue: [ triggerChannel removeDependent: self ].
  129.  
  130.       (super dependents notNil)
  131.          ifTrue: [ self unhookFromSubject ].
  132.  
  133.       super releaseParts
  134. |
  135.    subject
  136.  
  137.       " The subject of our adapting logic.  The saved value will
  138.       * be sent to the subject when the trigger channel indicates
  139.       * that it is time to do so.
  140.       "
  141.       ^ subject
  142. |
  143.    subject: aValueModel
  144.  
  145.       " The subject is the actual respository for the value
  146.       * held in this object.
  147.       "
  148.       (subject notNil and: [super dependents notNil])
  149.           ifTrue: [ self unhookFromSubject ].
  150.  
  151.       subject <- aValueModel.
  152.  
  153.       self value: na notYetAssigned.
  154.  
  155.       (subject notNil and: [super dependents notNil])
  156.           ifTrue: [ self hookupToSubject ]
  157. |
  158.    triggerChannel
  159.       " The object we depend on which sends an update message
  160.       * when the saved value should be inserted into the subject.
  161.       "
  162.       ^ triggerChannel
  163. |
  164.    triggerChannel: aValueModel
  165.       " An object to depend on which will send an update message
  166.       * to trigger the copy of the saved value to the subject.
  167.       "
  168.       (triggerChannel notNil)
  169.          ifTrue: [ triggerChannel removeDependent: self ].
  170.  
  171.       triggerChannel <- aValueModel.
  172.  
  173.       (triggerChannel notNil)
  174.          ifTrue: [ triggerChannel addDependent: self ]
  175. |
  176.    value
  177.  
  178.       " Answer the current value. "
  179.  
  180.       (value == na notYetAssigned)
  181.          ifTrue: [ ^ subject value ]
  182.         ifFalse: [ ^ value ]
  183. |
  184.    valueUsingSubject: aSubject
  185.  
  186.       ^ subject valueUsingSubject: aSubject
  187. |
  188.    addDependent: anObject
  189.  
  190.       " Add anObject as one of the receiver's dependents. "
  191.  
  192.       (super dependents == nil) 
  193.          ifTrue: [self hookupToSubject].
  194.  
  195.       ^ super addDependent: anObject
  196. |
  197.    removeDependent: anObject
  198.  
  199.       " Remove the argument, anObject, as one of the
  200.       * receiver's dependents.
  201.       "
  202.       super removeDependent: anObject.
  203.       
  204.       (super dependents == nil) 
  205.          ifTrue: [self unhookFromSubject].
  206.       
  207.       ^ anObject
  208. |
  209.    changedTrigger
  210.  
  211.       " Process the trigger notification. "
  212.       " Unhooking and rehooking the subject prevents 
  213.       * dependency notification being propogated thru this object.
  214.       * The dependents of this object have already been informed
  215.       * of the current value.
  216.       "
  217.       " There's nothing to do if no new value has been set
  218.       * since the last trigger.
  219.       * Save or reset?
  220.       "
  221.       (self triggerChannel value)
  222.       
  223.          ifTrue: [ " Send the buffered value to the subject. "
  224.                    (value == na notYetAssigned) 
  225.                       ifTrue: [ ^ self ].
  226.                    
  227.                    self unhookFromSubject.
  228.                    self subject value: value.
  229.                    
  230.                    value <- na notYetAssigned.
  231.                    self hookupToSubject ]
  232.  
  233.         ifFalse: [ " Reset the buffered value to show thru the
  234.                    * subject's value.
  235.                    "
  236.                    value <- na notYetAssigned.
  237.                    
  238.                    super dependents update: #value 
  239.                                       with: #reset 
  240.                                       from: self   ]
  241. |
  242.    hookupToSubject
  243.  
  244.       " Add the receiver as a dependent of the
  245.       * receiver's subject.
  246.       "
  247.       subject addDependent: self
  248. |
  249.    renderingValueUsingSubject: aSubject
  250.  
  251.       ^ subject renderingValueUsingSubject: aSubject
  252. |
  253.    unhookFromSubject
  254.  
  255.       " Remove the receiver as a dependent of the
  256.       * receiver's subject.
  257.       "
  258.       subject removeDependent: self
  259. |
  260.    update: anAspect with: parameters from: anObject
  261.  
  262.       (anObject == triggerChannel)
  263.  
  264.          ifTrue: [ self changedTrigger ]
  265.  
  266.         ifFalse: [ (anObject == subject)
  267.                    ifTrue: [ (value == na notYetAssigned) 
  268.                              ifTrue: [ super dependents update: anAspect 
  269.                                              with: parameters 
  270.                                              from: self ] ]
  271.  
  272.                   ifFalse: [ super update: anAspect 
  273.                                      with: parameters 
  274.                                      from: anObject ] ]
  275. |
  276.    isBuffering
  277.  
  278.       " Answer true if a value is being buffered "
  279.  
  280.       ^ value ~~ na notYetAssigned
  281. ]
  282.